﻿using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.Text;
using System.Threading;

namespace SharpCommunications.Logging
{
	public interface ILog
	{
	    void Message(string sLogCategoryName, Location location, string sMessage, params object[] messageArgs);
		void DebugMessage(Location location, string sMessage, params object[] messageArgs);
		void InfoMessage(Location location, string sMessage, params object[] messageArgs);
		void WarningMessage(Location location, string sMessage, params object[] messageArgs);
		void ErrorMessage(Location location, string sMessage, params object[] messageArgs);
		void ErrorMessage(Location location, Exception ex, bool bAppendStackTrace);
	}

    public static class LogFactory
    {
        private static object syncRoot = new object();

        private static ILog log = null;

        public static ILog Log
        {
            get
            {
                lock (syncRoot)
                {
                    if (log == null)
                    {
                        log = new ConsoleLog();
                    }

                    return log;
                }
            }
        }
    }

    public class ConsoleLog : ILog
    {
        private static object syncRoot = new object();

        #region ILog Members

        public void Message(string sLogCategoryName, Location location, string sMessage, params object[] messageArgs)
        {
            lock (syncRoot)
            {
                Console.WriteLine("______________________");
                Console.WriteLine("{0} - {1} - {2}:", DateTime.Now, sLogCategoryName, location);

                if(!string.IsNullOrEmpty(Thread.CurrentThread.Name))
                {
                    Console.WriteLine("Thread Name: {0}", Thread.CurrentThread.Name);
                }

                Console.WriteLine(sMessage, messageArgs);
                Console.WriteLine(string.Empty);
            }
        }

        public void DebugMessage(Location location, string sMessage, params object[] messageArgs)
        {
            Message("DEBUG", location, sMessage, messageArgs);
        }

        public void InfoMessage(Location location, string sMessage, params object[] messageArgs)
        {
            Message("INFO", location, sMessage, messageArgs);
        }

        public void WarningMessage(Location location, string sMessage, params object[] messageArgs)
        {
            Message("WARNING", location, sMessage, messageArgs);
        }

        public void ErrorMessage(Location location, string sMessage, params object[] messageArgs)
        {
            Message("ERROR", location, sMessage, messageArgs);
        }

        public void ErrorMessage(Location location, Exception ex, bool bAppendStackTrace)
        {
            string message = ex.Message;

            if(bAppendStackTrace)
            {
                message += "\nSTACK TRACE:\n" + ex.StackTrace;
            }

            ErrorMessage(location, message);
        }

        #endregion
    }

    public class Location
    {

        public string m_sLocation;

        public Location()
        {
            StackTrace trace = new StackTrace(true);

            if (trace.FrameCount > 0)
            {
                StackFrame frame = trace.GetFrame(0); // current frame

                System.Reflection.MethodBase method = frame.GetMethod();

                if (method.Name == ".ctor" && method.DeclaringType.Name == "Location")
                {
                    frame = trace.GetFrame(1);
                    method = frame.GetMethod();
                }

                m_sLocation = string.Format("{0}: {1}", method.DeclaringType, method).Replace("+", ".");
            }
            else
            {
                m_sLocation = "Unknown";
            }
        }

        public Location(string sLocation)
        {
            m_sLocation = sLocation;
        }

        public Location(int nLevelIndex)
        {
            m_sLocation = GetLevel(nLevelIndex);
        }

        public override string ToString()
        {
            return m_sLocation;
        }

        public static implicit operator string(Location location)
        {
            return location.ToString();
        }


        public static string Here
        {
            get
            {
                StackTrace trace = new StackTrace(false);

                if (trace.FrameCount > 1)
                {
                    StackFrame frame = trace.GetFrame(1);
                    return string.Format("{0}.{1}", frame.GetMethod().DeclaringType.FullName, frame.GetMethod().Name).Replace("+", ".");
                }
                else
                {
                    StackFrame frame = trace.GetFrame(trace.FrameCount - 1);
                    return string.Format("{0}.{1}", frame.GetMethod().DeclaringType.FullName, frame.GetMethod().Name).Replace("+", ".");
                }
            }
        }


        public static string OneLevelUp
        {
            get
            {
                StackTrace trace = new StackTrace(false);

                if (trace.FrameCount > 2)
                {
                    StackFrame frame = trace.GetFrame(2);
                    return string.Format("{0}.{1}", frame.GetMethod().DeclaringType.FullName, frame.GetMethod().Name).Replace("+", ".");
                }
                else
                {
                    StackFrame frame = trace.GetFrame(trace.FrameCount - 1);
                    return string.Format("{0}.{1}", frame.GetMethod().DeclaringType.FullName, frame.GetMethod().Name).Replace("+", ".");
                }
            }
        }

        public static string TwoLevelsUp
        {
            get
            {
                StackTrace trace = new StackTrace(false);

                if (trace.FrameCount > 3)
                {
                    StackFrame frame = trace.GetFrame(3);
                    return string.Format("{0}.{1}", frame.GetMethod().DeclaringType.FullName, frame.GetMethod().Name).Replace("+", ".");
                }
                else
                {
                    StackFrame frame = trace.GetFrame(trace.FrameCount - 1);
                    return string.Format("{0}.{1}", frame.GetMethod().DeclaringType.FullName, frame.GetMethod().Name).Replace("+", ".");
                }
            }
        }

        public static string ThreeLevelsUp
        {
            get
            {
                StackTrace trace = new StackTrace(false);

                if (trace.FrameCount > 4)
                {
                    StackFrame frame = trace.GetFrame(4);
                    return string.Format("{0}.{1}", frame.GetMethod().DeclaringType.FullName, frame.GetMethod().Name).Replace("+", ".");
                }
                else
                {
                    StackFrame frame = trace.GetFrame(trace.FrameCount - 1);
                    return string.Format("{0}.{1}", frame.GetMethod().DeclaringType.FullName, frame.GetMethod().Name).Replace("+", ".");
                }
            }
        }

        public static string GetLevel(int nLevelIndex)
        {
            StackTrace trace = new StackTrace(false);

            if (trace.FrameCount > nLevelIndex)
            {
                StackFrame frame = trace.GetFrame(nLevelIndex);
                return string.Format("{0}.{1}", frame.GetMethod().DeclaringType.FullName, frame.GetMethod().Name).Replace("+", ".");
            }
            else
            {
                StackFrame frame = trace.GetFrame(trace.FrameCount - 1);
                return string.Format("{0}.{1}", frame.GetMethod().DeclaringType.FullName, frame.GetMethod().Name).Replace("+", ".");
            }
        }

    } 
    
    
}
